<?php
/**
 * The base dao object
 * Pair it with an adaptor!
 * @author bbqq <toughgamer@gmail.com>
 * @package com.blib.data
 * @copyright bbqq qstufie.com
 */
class Blib_Data_Dao_Base extends Blib_Class_Atomic
{
    /**
     * the db adaptor to delegate to
     * @var Blib_Interface_Data_Adaptor
     */
    protected $_adaptor = null;


    /**
     * construct the base...
     * @param $type currently only mysql available (which is using pdo)
     * @param $options
     * @return void
     */
    public function __construct($type, $options)
    {
        // set adaptor
        // parse the type first...
        $method = 'set' . ucfirst(strtolower($type)) . 'Adaptor';

        // call it now if exists
        if (method_exists($this, $method)) {
            $this->{$method}($options);
            return true;
        }

        // otherwise it's a gonner
        throw new Exception('Adaptor Type: ' . $type . ' not supported yet');

    }// end __construct


    // quote some string
    public function quote($str)
    {
        return $this->_adaptor->quote($str);

    }// end quote


    // quote names
    public function nameQuote($name)
    {
        return $this->_adaptor->nameQuote($name);

    }// end nameQuote


    /**
     * set up the adaptor - mysql
     * @param array $options
     * @return self
     */
    private function setMysqlAdaptor($options)
    {
        $this->_adaptor = new Blib_Data_Adaptor_Mysql($options);

    }// end setAdaptor


    /**
     * set up the adaptor - Sqlite
     * @param array $options
     * @return self
     */
    private function setSqliteAdaptor($options)
    {
        $this->_adaptor = new Blib_Data_Adaptor_Sqlite($options);

    }// end setAdaptor

/*-------------------------------------- protected functions --------------------------------------*/

    /**
     * (non-PHPdoc)
     * @see lib/dao/adaptor/Blib_Interface_Data_Adaptor#query($sql, $bind)
     */
    protected function query($sql, $bind = array())
    {
        return $this->_adaptor->query($sql, $bind);

    }// end query


    /**
     * (non-PHPdoc)
     * @see lib/dao/adaptor/Blib_Interface_Data_Adaptor#insert($data, $tableName)
     */
    protected function insert($data, $tableName)
    {
        return $this->_adaptor->insert($data, $tableName);

    }// end insert


    /**
     * added: replace into.
     * @see lib/dao/adaptor/Blib_Interface_Data_Adaptor#replaceInto($data, $tableName)
     */
    protected function replaceInto($data, $tableName)
    {
        return $this->_adaptor->replaceInto($data, $tableName);

    }// end replaceInto


    /**
     * (non-PHPdoc)
     * @see lib/dao/adaptor/Blib_Interface_Data_Adaptor#update($data, $tableName)
     */
    protected function update($data, $tableName, $condition)
    {
        return $this->_adaptor->update($data, $tableName, $condition);

    }// end update


    /**
     * (non-PHPdoc)
     * @see lib/dao/adaptor/Blib_Interface_Data_Adaptor#delete($data, $tableName)
     */
    protected function delete($tableName, $condition)
    {
        return $this->_adaptor->delete($tableName, $condition);

    }// end delete


    /**
     * (non-PHPdoc)
     * @see lib/dao/adaptor/Blib_Interface_Data_Adaptor#fetchOne($sql, $bind)
     */
    protected function fetchOne($sql, $bind = array(), $cache = false)
    {
        return $this->_adaptor->fetchOne($sql, $bind, $cache);

    }// end fetchOne


    /**
     * (non-PHPdoc)
     * @see lib/dao/adaptor/Blib_Interface_Data_Adaptor#fetchAll($sql, $bind)
     */
    protected function fetchAll($sql, $bind = array(), $cache = false)
    {
        return $this->_adaptor->fetchAll($sql, $bind, $cache);

    }// end fetchAll


    /**
     * (non-PHPdoc)
     * @see lib/dao/adaptor/Blib_Interface_Data_Adaptor#getRowCount()
     */
    protected function getRowCount()
    {
        return $this->_adaptor->getRowCount();

    }// end getRowCount


    /**
     * clear all cached queries
     * @return void
     */
    public function clearCache()
    {
        Blib::removeNs('_SQL_');

    }// end clearCache

    /*------------------------------------------[ object CRUD ]------------------------------------------*/

    /**
     * save one object
     *
     * @param Blib_Data_Model_Base $object the object to save
     *
     * @return bool
     */
    final public function saveObject(Blib_Data_Model_Base $object)
    {
    	// simple as...
    	if ($object->isNew()) {
    		$success = $this->insert($object->__getRawData(), $object->__getTableName());
            // get last insert id if the id is int
            $id = (int) $this->_adaptor->getLastInsertId();
            if ($id > 0) {
                $object->setID($id);
            }
    		return $success;
    	} else {
    		// slightly more complex...
    		$data  = $object->__getRawData();
    		$id    = $object->getID();
    		$where = $this->_getCondition($object);
    		return $this->update($data, $object->__getTableName(), $where);
    	}

    }// end saveObject


    /**
     * read one object
     *
     * @param Blib_Data_Model_Base $object
     * @param str        $condition if not empty, ignore the id
     *
     * @return Blib_Data_Model_Base
     */
    final public function readObject(Blib_Data_Model_Base $object, $condition = '')
    {
    	// must make sure it's old...
        //* NOT necessary...
    	if ($object->isNew()) {
    		throw new Blib_Exception_Model($this, $object,
    		          'Object is not saved in the database');
    	}
        //*/

        // sql
        $sql = 'SELECT
    	           *
    	        FROM
    	           ' . $this->nameQuote($object->__getTableName()). '
    	        WHERE ';

    	// otherwise, read it
        if (!empty($condition)) {
            $sql .= $condition;
        } else {
    	   $sql .= $this->_getCondition($object);
        }

    	// run it...
    	$results = $this->fetchOne($sql);

    	if (!empty($results)) {
    		$object->setup($results);
    	} else {
    		return null;
    	}

    	return $object;

    }// end readObject


    /**
     * calculate orders
     *
     * @param array $orderby
     *
     * @return str
     */
    protected function _getOrderBy($orderby = array())
    {
        $orders = null;
        if (is_array($orderby) && count($orderby) > 0) {
            $orders = array();
            foreach ($orderby as $field => $order) {
                $orders[] = ' ' . $this->nameQuote($field) . ' ' . $order;
            }
            $orders = ' ORDER BY ' . join(', ', $orders);
        }

        return $orders;

    }// end _getOrderBy


    /**
     * read a list of object
     *
     * @param Blib_Data_Model_Base $object
     * @param str                                   $condition the condition string
     * @param int                                   $start     start
     * @param int                                   $limit     limit
     *
     * @return array(Blib_Data_Model_Base)
     */
    final public function readObjects(Blib_Data_Model_Base $object, $start = 0, $limit = 100,
                                      $condition = null, $orderby = array(), $bind = null)
    {
    	if (!empty($condition)) {
    		$condition = ' WHERE ' . $condition . ' ';
    	}

    	$className = get_class($object);
        $orders    = $this->_getOrderBy($orderby);

    	$limits = ' LIMIT ' . $start . ', ' . $limit;
    	$sql    = 'SELECT
	    	          *
	               FROM
	                  ' . $this->nameQuote($object->__getTableName()).
    	           $condition .
    	           $orders .
    	           $limits;

        $arResults = $this->fetchAll($sql, $bind);
        unset($object);

        // build objects
        $objects = array();
        if (!empty($arResults)) {
        	foreach ($arResults as $result) {
                $tmpObj = new $className($result);
                $objects[$tmpObj->getID(true)] = $tmpObj;
                unset($tmpObj);
        	}
        }

        return $objects;

    }// end readObjects


    /**
     * delete one object
     *
     * @param Blib_Data_Model_Base $object the object to delete
     *
     * @return bool
     */
    final public function deleteObject(Blib_Data_Model_Base $object)
    {
    	// it must be deletable - means it must have isNew as false
    	if ($object->isNew()) {
    		throw new Blib_Exception_Model($this, $object,
    		          'Trying to delete an object that doesn\'t exist in the database.');
    	}

    	// otherwise, do it...
    	return $this->delete($object->__getTableName(), $this->_getCondition($object));

    }// end deleteObject


    /**
     * get the condition by object
     *
     * @param Blib_Data_Model_Base $object the object to save
     *
     * @access private
     * @return str
     */
    private function _getCondition(Blib_Data_Model_Base $object)
    {
    	$where = null;

    	if ($id = $object->getID()) {
            if (!is_array($id)) {
                // then simple where:
                $where = $this->nameQuote($object->__getUniqueIdName()) . ' = ' . $this->quote($id);
            } else {
                // more complex as an array...
                foreach ($id as $field => $value) {
                    $where[] = $this->nameQuote($field) . ' = ' . $this->quote($value);
                }

                $where = join(' AND ', $where);
            }
    	}

    	return $where;

    }// end _getCondition

}// end Blib_Data_Dao_Base
?>